local _, playerClass = UnitClass("player")
local playerHealer = (playerClass == "PRIEST") or (playerClass == "SHAMAN") or (playerClass == "PALADIN") or (playerClass == "DRUID")
local playerCaster = (playerClass == "MAGE") or (playerClass == "PRIEST") or (playerClass == "WARLOCK")
local playerMelee = (playerClass == "ROGUE") or (playerClass == "WARRIOR") or (playerClass == "HUNTER")
local playerHybrid = (playerClass == "DEATHKNIGHT") or (playerClass == "DRUID") or (playerClass == "PALADIN") or (playerClass == "SHAMAN")

--Libraries
local L = AceLibrary("AceLocale-2.2"):new("DrDamage")
local GT = LibStub:GetLibrary("LibGratuity-3.0")
local DrDamage = DrDamage

--General
local settings
local _G = getfenv(0)
local type = type
local pairs = pairs
local ipairs = ipairs
local tonumber = tonumber
local math_floor = math.floor
local math_min = math.min
local math_max = math.max
local string_match = string.match
local string_format = string.format
local string_find = string.find
local string_sub = string.sub
local string_gsub = string.gsub
local string_len = string.len
local select = select
local next = next

--Module
local GameTooltip = GameTooltip
local UnitBuff = UnitBuff
local UnitDebuff = UnitDebuff
local UnitLevel = UnitLevel
local UnitDamage = UnitDamage
local UnitPower = UnitPower
local UnitPowerMax = UnitPowerMax
local UnitHealth = UnitHealth
local UnitHealthMax = UnitHealthMax
local UnitAttackPower = UnitAttackPower
local UnitIsUnit = UnitIsUnit
local UnitClassification = UnitClassification
local UnitExists = UnitExists
local UnitIsPlayer = UnitIsPlayer
local BOOKTYPE_SPELL = BOOKTYPE_SPELL
local GetSpellName = GetSpellName
local GetSpellInfo = GetSpellInfo
local GetMacroSpell = GetMacroSpell
local GetActionInfo = GetActionInfo
local GetCursorInfo = GetCursorInfo
local GetInventoryItemLink = GetInventoryItemLink
local GetItemInfo = GetItemInfo
local GetItemGem = GetItemGem
local GetTime = GetTime
local GetWeaponEnchantInfo = GetWeaponEnchantInfo
local GetTalentInfo = GetTalentInfo
local GetNumTalentTabs = GetNumTalentTabs
local GetNumTalents = GetNumTalents
local GetGlyphSocketInfo = GetGlyphSocketInfo
local GetCombatRatingBonus = GetCombatRatingBonus
local HasAction = HasAction
local IsEquippedItem = IsEquippedItem
local IsAddOnLoaded = IsAddOnLoaded
local IsAltKeyDown = IsAltKeyDown
local IsControlKeyDown = IsControlKeyDown
local IsShiftKeyDown = IsShiftKeyDown
local SecureButton_GetModifiedAttribute = SecureButton_GetModifiedAttribute
local SecureButton_GetEffectiveButton = SecureButton_GetEffectiveButton
local ActionButton_GetPagedID = ActionButton_GetPagedID
local InCombatLockdown = InCombatLockdown

--Module variables
local playerCompatible
local spellInfo, talentInfo, talents, PlayerHealth, TargetHealth
local updatingSpell, updateSetItems
local dmgMod
local DrD_Font = GameFontNormal:GetFont()
DrDamage.visualChange = true

--Local functions
function DrDamage.ClearTable(table)
	for k in pairs( table ) do
		table[k] = nil
	end
end
function DrDamage.Round(x, y)
	return math_floor( x * 10 ^ y + 0.5 ) / 10 ^ y
end
function DrDamage.MatchData( data, ... )
	if not data or not ... then
		return false
	end

	if type( data ) == "table" then
		for i = 1, select('#', ...) do
			if data[1] then
				for _, dataName in ipairs( data ) do
					for i = 1, select('#', ...) do
						if dataName == select(i, ...) then
							return true
						end
					end
				end
			else
				if data[select(i, ...)] then
					return true
				end
			end
		end
	else	
		for i = 1, select('#', ...) do
			if data == select(i, ...) then
				return true
			end
		end
	end
	
	return false
end

local DrD_ClearTable = DrDamage.ClearTable
local DrD_Round = DrDamage.Round
local DrD_MatchData = DrDamage.MatchData

local function DrD_Set( n, set, change )
	return function(v) 
		settings[n] = v
		if change then
			DrDamage.visualChange = true
		end
		if not set and not DrDamage:IsEventScheduled("DrD_FullUpdate") then
			DrDamage:CancelSpellUpdate()
			DrDamage:ScheduleEvent("DrD_FullUpdate", DrDamage.UpdateAB, 0.5, DrDamage)
		end
	end
end

local ABfunc, ABdisable
local ABtable, ABobjects, ABrefresh = {}, {}, {}

local function DrD_DetermineAB()
	--Default
	for i = 1, 6 do
		for j = 1, 12 do
			table.insert(ABobjects,_G[((select(i,"ActionButton", "MultiBarBottomLeftButton", "MultiBarBottomRightButton", "MultiBarRightButton", "MultiBarLeftButton", "BonusActionButton"))..j)])
		end
	end
	if IsAddOnLoaded("Bartender4") then
		local func = function(button) if button.Secure then return button.Secure:GetActionID() else return button:GetActionID() end end
		ABrefresh["BT4Button"] = function()
			for i=1,120 do
				ABtable["BT4Button"..i] = func
			end
		end
		ABdisable = true
	end
	if nUI_DrDamageIntegration then
		ABrefresh["nUI"] = nUI_DrDamageIntegration( ABtable )
		ABdisable = true
	end
	if IsAddOnLoaded("Nurfed") then
		local func = function(button)
			if button.spell then
				if button.type == "spell" then
					local pid = Nurfed:getspell(button.spell)
					if pid then
						return nil, GetSpellName(pid, BOOKTYPE_SPELL)
					end
				elseif button.type == "macro" then
					local action, rank = GetMacroSpell(button.spell)
					if action then 							
						return nil, action, rank
					end
				end
			end
		end	
		ABrefresh["Nurfed_Button"] = function()
			for i=1,120 do
				ABtable["Nurfed_Button"..i] = func
			end
		end
		ABdisable = true
	end
	if IsAddOnLoaded( "FlexBar2" ) then
		ABfunc = function()
			for _, button in pairs(FlexBar2.Buttons) do
				button:UpdateTextSub("drd")
			end
		end
	end	
	if IsAddOnLoaded("Macaroon") then
		local func = function(button)
			if button.config and button.config.type == "action" then
				return SecureButton_GetModifiedAttribute(button,"action",SecureButton_GetEffectiveButton(button))
			end
			if button.macroshow then
				return nil, string_match(button.macroshow,"[^%(]+"), button.macrorank
			end
			if button.macrospell then
				return nil, string_match(button.macrospell,"[^%(]+"), button.macrorank
			end
		end
		ABrefresh["MacaroonButton"] = function()
			for _, button in pairs(Macaroon.Buttons) do
				ABtable[button[1]:GetName()] = func
			end
		end
	end	
	if IsAddOnLoaded("Dominos") then
		local func = function(button) return SecureButton_GetModifiedAttribute(button,"action",SecureButton_GetEffectiveButton(button)) end
		ABrefresh["DominosActionButton"] = function()
			for i=1,120 do
				ABtable["DominosActionButton"..i] = func
			end
		end
	end
	if IsAddOnLoaded("IPopBar") then
		local func = function(button) return SecureButton_GetModifiedAttribute(button,"action",SecureButton_GetEffectiveButton(button)) end
		ABrefresh["IPopBarButton"] = function()
			for i=1,120 do
				ABtable["IPopBarButton"..i] = func
			end
		end
	end
	if IsAddOnLoaded("CT_BarMod") then
		--There's an object list available CT_BarMod.actionButtonList, index = actionID, value.hasAction
		local func = function(button) return button.object.id end
		ABrefresh["CT_BarModActionButton"] = function()
			for i=13,24 do
				ABtable["CT_BarModActionButton"..i] = func
			end
			for i=49,120 do
				ABtable["CT_BarModActionButton"..i] = func
			end			
		end
	end
	if IsAddOnLoaded("RDX") then
		local func = function(button) return button:GetAttribute("action") end
		ABrefresh["RDX_ActionBars"] = function()
			for i=1,120 do
				ABtable["VFLButton"..i] = func
			end
		end
	end
	if not next(ABrefresh) then
		ABrefresh, ABtable = nil, nil
	end
	DrDamage:RefreshAB()
end

function DrDamage:RefreshAB()
	if ABrefresh then
		for k in pairs(ABtable) do
			ABtable[k] = nil
		end
		for _, func in pairs(ABrefresh) do
			func()
		end
	end
end

--Defaults
local defaults = DrDamage.defaults
defaults.ABText = true
defaults.DefaultAB = true
defaults.FontEffect = "OUTLINE"
defaults.FontSize = 10
defaults.FontXPosition = 0
defaults.FontYPosition = 0
defaults.FontColorDmg = { r = 1, g = 1, b = 0.2 }
defaults.FontColorHeal = { r = 0.4, g = 1.0, b = 0.3 }
defaults.UpdateShift = false
defaults.UpdateAlt = false
defaults.UpdateCtrl = false
defaults.AlwaysTooltip = false
defaults.CombatTooltip = false
defaults.NeverTooltip = false
defaults.AltTooltip = false
defaults.CtrlTooltip = false
defaults.ShiftTooltip = true
defaults.DefaultColor = false
defaults.SwapCalc = false
defaults.PlayerAura = {}
defaults.TargetAura = {}
defaults.Consumables = {}
DrDamage:RegisterDB("DrDamageDB", "DrDamageDB", "class");
DrDamage:RegisterDefaults("profile", defaults)
DrDamage.options = { type='group', args = {} }
DrDamage:RegisterChatCommand({ "/drdmg", "/drdamage" }, DrDamage.options)

--FuBar options
if AceLibrary:HasInstance("FuBarPlugin-2.0") then
	DrDamage.name = "DrDamage"
	DrDamage.title = "Dr. Damage"
	DrDamage.hasNoColor = true
	DrDamage.hasNoText = true
	DrDamage.cannotDetachTooltip = true
	DrDamage.hideWithoutStandby = true
	DrDamage.independentProfile = true
	DrDamage.blizzardTooltip = true
	DrDamage.hasIcon = playerMelee and "Interface\\Icons\\Ability_DualWield" or "Interface\\Icons\\Spell_Holy_SearingLightPriest"
	DrDamage.OnMenuRequest = DrDamage.options
end

function DrDamage:OnTooltipUpdate()
	GameTooltip:AddLine( "          Dr. Damage" )
	GameTooltip:AddLine( "Hint: Right-Click for options", 0, 1, 0, true )
end

function DrDamage:OnInitialize()
	settings = self.db.profile	
end

function DrDamage:OnEnable()
	if IsAddOnLoaded("FlexBar2") and FlexBar2:HasModule("TextSubs") then
		FlexBar2:GetModule("TextSubs"):RegisterTextSub("drd", 
		function(button)
			if not playerCompatible then return false end
			if not settings.ABText then return "" end
			local spell = button:GetModifiedAttribute("spell");
			if(spell and spell ~= "") then
				-- Run the spell through GetSpellName to seperate Name & Rank and get the rank if its not specified
				local name, rank = GetSpellName(spell)
				return self:CheckAction(nil, nil, nil, name, rank)
			end
		end)		
	end
	if self.PlayerData then
		self.ClassSpecials = {}
		self.DmgCalculation = {}
		self.SetBonuses = {}
		self.RelicSlot = {}
		self.talents = {}
		self:PlayerData()
		self.PlayerData = nil
		PlayerHealth = self.PlayerHealth
		TargetHealth = self.TargetHealth
		spellInfo = self.spellInfo
		talentInfo = self.talentInfo
		talents = self.talents
		self:CommonData()
		self.CommonData = nil		
	elseif not self.spellInfo and not self.talentInfo then
		return
	end
	
	self:OnProfileEnable()
	
  	if AceLibrary("AceEvent-2.0"):IsFullyInitialized() then
  		self:Load()
  	else
  		self:RegisterEvent("PLAYER_ENTERING_WORLD", function() self:ScheduleEvent("DrD_Load", self.Load, 2, self) end, true)
  	end
end

function DrDamage:CommonData()
	if select(2, UnitRace("player")) == "Draenei" then
		if playerClass == "PALADIN" or playerClass == "PRIEST" or playerClass == "SHAMAN" or playerClass == "MAGE" or playerClass == "DEATHKNIGHT" then
			--Gift of the Naaru
			spellInfo[GetSpellInfo(28880)] = {
						["Name"] = "Gift of the Naaru",
						[0] = { School = { "Holy", "Healing" }, Cooldown = 180, bonusFactor = 1, eDot = true, eDuration = 15, sTicks = 3 },
						[1] = { 35, 35, },
			}
			self.Calculation["Gift of the Naaru"] = function( calculation )
				local b, p, n = UnitAttackPower("player")
				local ap = b + p + n
				if ap > calculation.spellDmg then calculation.spellDmg = ap end
				calculation.minDam = calculation.minDam + UnitLevel("player") * 15
				calculation.maxDam = calculation.minDam
			end
		elseif playerClass == "WARRIOR" or playerClass == "HUNTER" then
			self.ClassSpecials[GetSpellInfo(28880)] = function()
				local amount = 35 + UnitLevel("player") * 15 + self:GetAP()
				return amount, 0.4, 1.0, 0.3
			end
		end
	end
	if select(2, UnitRace("player")) == "BloodElf" then
		if playerClass == "PALADIN" or playerClass == "PRIEST" or playerClass == "MAGE" or playerClass == "HUNTER" or playerClass == "WARLOCK" then
			--Arcane Torrent
			self.ClassSpecials[GetSpellInfo(28730)] = function()
				return 0.06 * UnitPowerMax("player",0), 0.40, 0.8, 1
			end
		end
	end
end

function DrDamage:Load()
	if self.GeneralOptions then
		self:GeneralOptions()
		self.GeneralOptions = nil
	end
	if self.Caster_OnEnable then
		self:Caster_OnEnable()
		self.Caster_OnEnable = nil
	end
	if self.Melee_OnEnable then
		self:Melee_OnEnable()
		self.Melee_OnEnable = nil
	end
	if DrD_DetermineAB then 
		DrD_DetermineAB()
		DrD_DetermineAB = nil
	end
	if not playerCompatible then
		--Make talent spell tables better suited for iteration
		for k in pairs( talentInfo ) do
			local talent = talentInfo[k]
			for i=1,#talent do
				local spelltable = talent[i].Spells
				if type(spelltable) == "table" then
					for j,v in ipairs( spelltable ) do
						spelltable[v] = true
						spelltable[j] = nil
					end
				end
			end
		end
		--Create talent tables
		for k, v in pairs( spellInfo ) do
			v["Talents"] = {}
			if v["Secondary"] then
				v["Secondary"]["Talents"] = {}
			end
		end
	end
	dmgMod = select(7, UnitDamage("player"))
	self:MetaGems()
	self:UpdateGlyphs()
	self:UpdateTalents()
	playerCompatible = true
	
	self:Hook(GameTooltip, "SetAction", true)
	self:SecureHook(GameTooltip, "SetSpell")  
	self:RegisterEvent("AceDB20_ResetDB")
  	self:RegisterEvent("CHARACTER_POINTS_CHANGED","UPDATE_TALENTS")
  	self:RegisterEvent("PLAYER_TALENT_UPDATE","UPDATE_TALENTS")
	self:RegisterEvent("GLYPH_ADDED", "UpdateGlyphs")
	self:RegisterEvent("GLYPH_UPDATED", "UpdateGlyphs")
	self:RegisterEvent("GLYPH_REMOVED", "UpdateGlyphs")
	self:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
		
	if settings.ABText then
		if settings.UpdateAlt or settings.UpdateCtrl or settings.UpdateShift then
			self:RegisterEvent( "MODIFIER_STATE_CHANGED" )
		end
		if PlayerHealth or TargetHealth then
			self:RegisterBucketEvent("UNIT_HEALTH", 2.5)
		end
		if (playerClass == "SHAMAN" or playerClass == "WARLOCK" or playerClass == "ROGUE" or playerClass == "DEATHKNIGHT") then
			self:RegisterBucketEvent("UNIT_INVENTORY_CHANGED", 1.5)
		end		
		self:RegisterEvent("ACTIONBAR_PAGE_CHANGED")
		self:RegisterEvent("ACTIONBAR_HIDEGRID")
		self:RegisterEvent("EXECUTE_CHAT_LINE")
		self:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
		self:RegisterEvent("PLAYER_TARGET_CHANGED")
		self:RegisterEvent("UPDATE_MACROS")
		self:RegisterEvent("LEARNED_SPELL_IN_TAB")
		self:RegisterEvent("UNIT_AURA")
	end
end

function DrDamage:OnDisable()
	if playerCompatible then
		local tempType = settings.ABText
		settings.ABText = false
		self:UpdateAB()
		settings.ABText = tempType
	end	
end

function DrDamage:OnProfileEnable()
	if self.Caster_OnProfileEnable then self:Caster_OnProfileEnable() end
	if self.Melee_OnProfileEnable then self:Melee_OnProfileEnable() end
	settings = self.db.profile
end

function DrDamage:GeneralOptions()
	self.options.args.General = {
		type = "group", 
		name = L["General"],
		desc = L["General options."], 
		order = 1,
		args = {
			ABText = {
				type = 'toggle',
				name = L["Actionbar text on/off"],
				desc = L["Actionbar text on/off"],
				order = 5,
				get = function() return settings["ABText"] end,
				set = DrD_Set("ABText"),
			},		
			DefaultAB = {
				type = 'toggle',
				name = L["Default actionbar support"],
				desc = L["Default actionbar support"],
				order = 10,
				get =  function() return settings["DefaultAB"] end,
				set =  function(v) 
						self.visualChange = true
						if v then
							settings.DefaultAB = true
							self:UpdateAB()
						else
							self:UpdateAB(nil, nil, true)
							settings.DefaultAB = false
						end
					end,				
			},
			SwapCalc = {
				type = 'toggle',
				name = L["Swap calculation for double purpose abilities"],
				desc = L["Swap calculation for double purpose abilities"],
				order = 15,
				get = function() return settings["SwapCalc"] end,
				set = DrD_Set("SwapCalc", false, true),
			},		
			FontSize = {
				type = 'range',
				name = L["Actionbar text size"],
				desc = L["Actionbar text size"],
				min = 6,
				max = 20,
				step = 1,
				order = 30,
				get =  function() return settings["FontSize"] end,
				set = DrD_Set("FontSize", false, true),
			},
			FontEffect = {
				type = "text",
				name = L["Actionbar text effect"],
				desc = L["Actionbar text effect"],
				validate = { L["Outline"], L["ThickOutline"], L["None"] },
				order = 40,
				get =	function()
	
						if settings.FontEffect == "" then
							return L["None"]
						elseif settings.FontEffect == "THICKOUTLINE" then
							return L["ThickOutline"]
						else
							return L["Outline"]
						end
					end,
				set = 	function(v)
						if v == L["None"] then
							settings.FontEffect = ""
						elseif v == L["ThickOutline"] then
							settings.FontEffect = "THICKOUTLINE"
						else
							settings.FontEffect = "OUTLINE"
						end

						self.visualChange = true
						self:UpdateAB()
					end,
			},				
			FontXPosition = {
				type = 'range',
				name = L["Actionbar text horizontal position"],
				desc = L["Actionbar text horizontal position"],
				min = -5,
				max = 5,
				step = 1,
				order = 50,
				get =  function() return settings["FontXPosition"] end,
				set = 	DrD_Set("FontXPosition", false, true),
			},
			FontYPosition = {
				type = 'range',
				name = L["Actionbar text vertical position"],
				desc = L["Actionbar text vertical position"],
				min = -15,
				max = 30,
				step = 1,
				order = 60,
				get =  function() return settings["FontYPosition"] end,
				set = 	DrD_Set("FontYPosition", false, true),
			},
			FontColorDmg = {
				type = "text",
				name = L["Damage text color"],
				desc = L["Set the actionbar damage text color"],
				usage = L["<r=n, g=n, b=n> where n is 0-1"],
				order = 70,
				get = function()
					return "r="..settings.FontColorDmg.r..", g="..settings.FontColorDmg.g..", b="..settings.FontColorDmg.b
				end,
				set = function(colors)
					for k, v in string.gmatch(string.lower(colors), "([r|g|b])=([%d\.]+)") do
						if tonumber(v) >= 0 and tonumber(v) <= 1 then
							settings.FontColorDmg[k] = v
						end
					end
					self.visualChange = true
					self:UpdateAB()						
				end
			},
			Update = {
				type = 'group', 
				name = L["Update"],
				desc = L["Update"],
				order = 90,
				args = {
					Update = {
						name = L["Update"],
						desc = L["Forces an update to the actionbar."],
						type = "execute",
						order = 1,
						func = function() self.visualChange = true, self:UpdateAB() end,
					},
					UpdateShift = {
						type = 'toggle',
						name = L["Update the actionbar with shift key"],

						desc = L["Update the actionbar with shift key"],
						order = 2,
						get =  function() return settings["UpdateShift"] end,
						set =  function(v)
								settings["UpdateShift"] = v
								if v then
									if not self:IsEventRegistered("MODIFIER_STATE_CHANGED") then
										self:RegisterEvent("MODIFIER_STATE_CHANGED")
									end
								elseif not settings.UpdateAlt and not settings.UpdateCtrl then
									self:UnregisterEvent("MODIFIER_STATE_CHANGED")
								end
							end,				
					},
					UpdateAlt = {
						type = 'toggle',
						name = L["Update the actionbar with alt key"],
						desc = L["Update the actionbar with alt key"],
						order = 3,
						get =  function() return settings["UpdateAlt"] end,
						set =  function(v)
								settings["UpdateAlt"] = v
								if v then
									if not self:IsEventRegistered("MODIFIER_STATE_CHANGED") then
										self:RegisterEvent("MODIFIER_STATE_CHANGED")
									end
								elseif not settings.UpdateCtrl and not settings.UpdateShift then
									self:UnregisterEvent("MODIFIER_STATE_CHANGED")
								end
							end,				
					}, 
					UpdateCtrl = {
						type = 'toggle',
						name = L["Update the actionbar with ctrl key"],
						desc = L["Update the actionbar with ctrl key"],
						order = 4,
						get =  function() return settings["UpdateCtrl"] end,
						set =  function(v)
								settings["UpdateCtrl"] = v
								if v then
									if not self:IsEventRegistered("MODIFIER_STATE_CHANGED") then
										self:RegisterEvent("MODIFIER_STATE_CHANGED")
									end
								elseif not settings.UpdateAlt and not settings.UpdateShift then
									self:UnregisterEvent("MODIFIER_STATE_CHANGED")
								end
							end,				
					},
				},
			},
			DisplayTooltip = {
				type = 'group',
				name = L["Display tooltip"],
				desc = L["Control when the tooltip is displayed."],
				order = 100,
				args = {
					Always = {
						type = 'toggle',
						name = L["Always"],
						desc = L["Always display the tooltip."],
						order = 1,
						get = function() return settings["AlwaysTooltip"] end,
						set = function(v)
							settings.AlwaysTooltip = v
							settings.NeverTooltip = not v
							settings.ShiftTooltip = false
							settings.CtrlTooltip = false
							settings.AltTooltip = false
							if v then
								settings.CombatTooltip = false
							end
	
						end,
					},
					Combat = {
						type = 'toggle',
						name = L["Disable in combat"],
						desc = L["Disables tooltip display in combat without modifier key. Set a modifier key if needed."],
						order = 3,
						get = function() return settings["CombatTooltip"] end,
						set = function(v)
							settings.CombatTooltip = v
							settings.NeverTooltip = false
							if v then
								settings.AlwaysTooltip = false
							elseif not settings.CtrlTooltip and not settings.ShiftTooltip and not settings.AltTooltip then
								settings.AlwaysTooltip = true
							end
						end,
					},					
					Never = {
						type = 'toggle',
						name = L["Never"],
						desc = L["Never display the tooltip."],
						order = 2,
						get = function() return settings["NeverTooltip"] end,
						set = function(v)
							settings.AlwaysTooltip = not v
							settings.NeverTooltip = v
							settings.CombatTooltip = false
							settings.ShiftTooltip = false
							settings.CtrlTooltip = false
							settings.AltTooltip = false
						end,
					},
					Alt = {
						type = 'toggle',
						name = L["With Alt"],
						desc = L["Display tooltip when Alt is pressed."],
						order = 4,
						get = function() return settings["AltTooltip"] end,
						set = function(v)
							settings.NeverTooltip = false
							settings.AltTooltip = v
							if v then
								settings.AlwaysTooltip = false
							elseif not settings.CtrlTooltip and not settings.ShiftTooltip and not settings.CombatTooltip then
								settings.AlwaysTooltip = true
							end
						end,
					},
					Ctrl = {
						type = 'toggle',
						name = L["With Ctrl"],
						desc = L["Display tooltip when Ctrl is pressed."],
						order = 5,
						get = function() return settings["CtrlTooltip"] end,
						set = function(v)
							settings.NeverTooltip = false
							settings.CtrlTooltip = v
							if v then
								settings.AlwaysTooltip = false
							elseif not settings.AltTooltip and not settings.ShiftTooltip and not settings.CombatTooltip then
								settings.AlwaysTooltip = true
							end
						end,
					},
					Shift = {
						type = 'toggle',
						name = L["With Shift"],
						desc = L["Display tooltip when Shift is pressed."],
						order = 6,
						get = function() return settings["ShiftTooltip"] end,
						set = function(v)
							settings.NeverTooltip = false
							settings.ShiftTooltip = v
							if v then
								settings.AlwaysTooltip = false
							elseif not settings.CtrlTooltip and not settings.AltTooltip and not settings.CombatTooltip then
								settings.AlwaysTooltip = true
							end
						end,
					},
					DefaultColor = {
						type = 'toggle',
						name = L["Default tooltip colors"],
						desc = L["Default tooltip colors"],
						order = 10,
						get = function() return settings["DefaultColor"] end,
						set = DrD_Set("DefaultColor", true),
					},					
				},
			},
			Aura = {
				type = 'group',
				name = L["Modify Buffs/Debuffs"],
				desc = L["Choose what buffs/debuffs to always include into calculations."],
				order = 170,
				args = {
					Player = {
						type = 'group',
						name = L["Player"],
						desc = L["Choose player buffs/debuffs."],
						args = {},
						},
					Target = {
						type = 'group',
						name = L["Target"],
						desc = L["Choose target buffs/debuffs."],
						args = {},
						},
					Consumables = {
						type = 'group',
						name = L["Consumables"],
						desc = L["Choose consumables to include."],
						args = {},
						},
				},
			},
			Talents = { 
				type = "group", 
				name = L["Modify Talents"], 
				desc = L["Modify talents manually. Modified talents are not saved between sessions."], 
				order = 200, 
				args = {
					Reset = { 
						type = "execute", 
						name = L["Reset Talents"],
						desc = L["Reset talents to your current talent configuration."],
						order = 1,
						func = function() self:UpdateTalents() end 
					},
				} ,
			},			
		},
	}
	local optionsTable = self.options.args.General.args
	local auraTable = optionsTable.Aura.args.Player.args
	for k,v in pairs( self.PlayerAura ) do
		if not v.Update and not v.NoManual then
			local manual = v.Manual
			if not manual or not auraTable[manual] then	
				auraTable[(manual or string_gsub(k," +", ""))] = {
					type = 'toggle',
					name = manual or k,
					desc = L["Include "] .. (manual or k),
					get = function() return settings["PlayerAura"][k] end,
					set = function(v) settings["PlayerAura"][k] = v or nil; self:UpdateAB() end,
				}
			else
				settings["PlayerAura"][k] = nil
			end
		else
			settings["PlayerAura"][k] = nil
		end
	end
	auraTable = optionsTable.Aura.args.Target.args
	for k,v in pairs( self.TargetAura ) do
		if not v.Update and not v.NoManual then
			local manual = v.Manual
			if not manual or not auraTable[manual] then
				auraTable[(manual or string_gsub(k," +", ""))] = {
					type = 'toggle',
					name = manual or k,
					desc = L["Include "] .. (manual or k),
					get = function() return settings["TargetAura"][k] end,
					set = function(v) settings["TargetAura"][k] = v or nil; self:UpdateAB() end,
				}
			else
				settings["TargetAura"][k] = nil
			end
		else
			settings["TargetAura"][k] = nil
		end
	end
	auraTable = optionsTable.Aura.args.Consumables.args
	for k,v in pairs( self.Consumables ) do
		auraTable[(string_gsub(k," +", ""))] = {
			type = 'toggle',
			name = k,
			desc = L["Include "] .. k,
			get = function() return settings["Consumables"][k] end,
			set = function(v) settings["Consumables"][k] = v or nil; self:UpdateAB() end,
		}
	end	
	if playerHealer then
		optionsTable.FontColorHeal = {
			type = "text",
			name = L["Heal text color"],
			desc = L["Set the actionbar heal text color."],
			usage = L["<r=n, g=n, b=n> where n is 0-1"],
			order = 80,
			get = function()
				return "r="..settings.FontColorHeal.r..", g="..settings.FontColorHeal.g..", b="..settings.FontColorHeal.b
			end,
			set = function(colors)
				for k, v in string.gmatch(string.lower(colors), "([r|g|b])=([%d\.]+)") do
					if tonumber(v) >= 0 and tonumber(v) <= 1 then
						settings.FontColorHeal[k] = v
					end
				end
				self.visualChange = true 
				self:UpdateAB()
			end
		}
	end
	local talentTable = optionsTable.Talents.args
	for t = 1, GetNumTalentTabs() do	
		for i = 1, GetNumTalents(t) do
			local talentName, _, _, _, _, maxRank = GetTalentInfo(t, i)
			if talentInfo[talentName] and not talentInfo[talentName].NoManual then
				talentTable[(string_gsub(talentName," +", ""))] = {
					type = 'range',
					name = talentName,
					desc = L["Modify "] .. talentName,
					min = 0,
					max = maxRank,
					step = 1,
					order = i + (t-1) * 50,
					get = 	function() return talents[talentName] or 0 end,
					set = 	function(v)
							local value = math_floor(v+0.5)
							talents[talentName] = (value == 0) and nil or value
							self:UpdateTalents(true)
						end,
				}
			end
		end
	end		
end

--EVENTS:
function DrDamage:CancelSpellUpdate()
	updatingSpell = nil
	self:CancelScheduledEvent("DrD_SpellUpdate")
end

--REQUIRES DELAY:
function DrDamage:ACTIONBAR_PAGE_CHANGED()
	self.visualChange = true
	if not self:IsEventScheduled("DrD_FullUpdate") then
		self:CancelSpellUpdate()
		self:ScheduleEvent("DrD_FullUpdate", self.UpdateAB, 0.2, self)
	end
end
function DrDamage:ACTIONBAR_HIDEGRID()
	self.visualChange = true
	if not self:IsEventScheduled("DrD_FullUpdate") and (GetCursorInfo() == "spell" or GetCursorInfo() == "macro") then
		self:CancelSpellUpdate()
		self:ScheduleEvent("DrD_FullUpdate", self.UpdateAB, 0.2, self)
	end
end
function DrDamage:UPDATE_SHAPESHIFT_FORM()
	self.visualChange = true
	if not self:IsEventScheduled("DrD_FullUpdate") then
		self:CancelSpellUpdate()
		self:ScheduleEvent("DrD_FullUpdate", self.UpdateAB, 1, self)
	end
end
function DrDamage:EXECUTE_CHAT_LINE()
	self:CancelScheduledEvent("DrD_StopMacroWatch")
	self:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
	self:ScheduleEvent("DrD_StopMacroWatch", self.UnregisterEvent, 0.5, self, "ACTIONBAR_SLOT_CHANGED")
end
function DrDamage:ACTIONBAR_SLOT_CHANGED(id)
	local gtype, pid = GetActionInfo(id)
	if gtype == "macro" then
		local name = GetMacroSpell(pid)
		if name and spellInfo[name] then
			self.visualChange = true
			if not self:IsEventScheduled("DrD_FullUpdate") then
				self:ScheduleEvent("DrD_SlotUpdate", self.UpdateAB, 0.5, self, nil, id)
			end
		end
	end
end

--INSTANT FULL UPDATES
function DrDamage:AceDB20_ResetDB( name, dab )
	if dab == self.db.name then
		self.visualChange = true
		self:UpdateAB()
	end
end

DrDamage.Damage_critMBonus = 0
DrDamage.Healing_critMBonus = 0
function DrDamage:MetaGems()
	self.Damage_critMBonus = 0
	self.Healing_critMBonus = 0
	local helm = GetInventoryItemLink("player", 1)
	if helm then
		for i = 1, 3 do 
			local mgem = tonumber(string_match(select(2, GetItemGem(helm, i)) or "",".-item:(%d+).*"))
			if mgem == 34220 or mgem == 41285 or mgem == 32409 or mgem == 41398 then
				if not self:IsMetaGemInactive() then
					self.Damage_critMBonus = 0.03
				end
				break
			elseif mgem == 41376 then
				if not self:IsMetaGemInactive() then
					self.Healing_critMBonus = 0.03
				end
				break
			end
		end
	end
end

function DrDamage:IsMetaGemInactive()
	GT:SetInventoryItem("player", 1)
	for j = 1, GT:NumLines() do 
		if GT:GetLine(j) and string_find(GT:GetLine(j), "|cff808080.*808080") then
			return true
		end
	end
end

--DELAY FOR EFFICIENCY
local mbuff, obuff
function DrDamage:UNIT_INVENTORY_CHANGED( units )
	if units["player"] then
		local buff = self:GetWeaponBuff()
		local buff2 = self:GetWeaponBuff(true)
		if buff ~= mbuff or buff2 ~= obuff then
			mbuff = buff
			obuff = buff2
			self:CancelScheduledEvent("DrD_FullUpdate")
			self:UpdateAB()
		end
	end
end

function DrDamage:PLAYER_EQUIPMENT_CHANGED(slot)
	updateSetItems = true
	if self.Melee_InventoryChanged then
		self:Melee_InventoryChanged((slot == 16), (slot == 17), (slot == 18))
	end
	if not self:IsEventScheduled("DrDamage_UpdateMetaGem") then
		self:ScheduleEvent("DrDamage_UpdateMetaGem", self.MetaGems, 0.20, self)
	end	
	if not self:IsEventScheduled("DrD_FullUpdate") then
		self:CancelSpellUpdate()
		self:ScheduleEvent("DrD_FullUpdate", self.UpdateAB, 0.25, self)
	end
end

local pHealth, tHealth = 1, 1
function DrDamage:UNIT_HEALTH(units)
	if PlayerHealth and units["player"] then
		local value = PlayerHealth[1]
		local health = UnitHealth("player")/UnitHealthMax("player")
		if (health < value) ~= (pHealth < value) then
			self:UpdateAB(PlayerHealth[value])
		end
		pHealth = health
	end
	if units["target"] then
		local health = UnitHealth("target")/UnitHealthMax("target")
		for i=1,#TargetHealth do
			local value = tonumber(TargetHealth[1]) or UnitHealth("player")/UnitHealthMax("player")
			if (health < value) ~= (tHealth < value) then
				if TargetHealth[value] then 
					self:UpdateAB(TargetHealth[value])
				else
					self:UpdateAB()
					tHealth = health
					return
				end
			end
		end
		tHealth = health
	end
end

function DrDamage:PLAYER_TARGET_CHANGED()
	if not self:IsEventScheduled("DrD_FullUpdate") then
		self:CancelSpellUpdate()
		self:ScheduleEvent("DrD_FullUpdate", self.UpdateAB, 0.5, self)
	end
	self:TargetAuraUpdate(true)
	tHealth = TargetHealth and (UnitHealth("target")/UnitHealthMax("target"))
end

function DrDamage:UPDATE_TALENTS()
	self.visualChange = true
	if not self:IsEventScheduled("DrD_TalentUpdate") then
		self:ScheduleEvent("DrD_TalentUpdate", self.UpdateTalents, 1, self)
	end
end

function DrDamage:UPDATE_MACROS()
	self.visualChange = true
	if not self:IsEventScheduled("DrD_FullUpdate") then
		self:CancelSpellUpdate()
		self:ScheduleEvent("DrD_FullUpdate", self.UpdateAB, 0.75, self)
	end
end

function DrDamage:LEARNED_SPELL_IN_TAB()
	if not self:IsEventScheduled("DrD_FullUpdate") then
		self:CancelSpellUpdate()
		self:ScheduleEvent("DrD_FullUpdate", self.UpdateAB, 1.5, self)
	end
end

function DrDamage:UNIT_AURA(unit)
	if unit == "player" then
		if not self:IsEventScheduled("DrD_PlayerAuraUpdate") then
			self:ScheduleEvent("DrD_PlayerAuraUpdate", self.PlayerAuraUpdate, 0.5, self)
		end
	elseif unit == "target" then
		if not self:IsEventScheduled("DrD_TargetAuraUpdate") then
			self:ScheduleEvent("DrD_TargetAuraUpdate", self.TargetAuraUpdate, 1, self)
		end	
	end
end

local PlayerAura = DrDamage.PlayerAura
local TargetAura = DrDamage.TargetAura
local oPlayerAura = {}
local nPlayerAura = {}
local oTargetAura = {}
local nTargetAura = {}

function DrDamage:PlayerAuraUpdate()
	if self:IsEventScheduled("DrD_FullUpdate") then
		return
	end	
	if dmgMod ~= select(7, UnitDamage("player")) then
		dmgMod = select(7, UnitDamage("player"))
		self:UpdateAB()
		return
	end
	if self.Caster_CheckBaseStats and self:Caster_CheckBaseStats() then
		self:UpdateAB()
		return
	end
	if self.Melee_CheckBaseStats and self:Melee_CheckBaseStats() then
		self:UpdateAB()
		return
	end
	--[[ Enable if ever needed
	if self.Calculation["PlayerAura"] then
		local update, spell = self.Calculation["PlayerAura"]
		if update then
			return self:UpdateAB(spell)
		end
	end
	--]]
	for i=1,40 do
		local name, rank, texture, count = UnitBuff("player",i)
		if name then
			if PlayerAura[name] then			
				nPlayerAura[name] = rank .. count
			end
		else
			break
		end
	end
	for i=1,40 do
		local name, rank, texture, count = UnitDebuff("player",i)
		if name then
			if PlayerAura[name] then
				nPlayerAura[name] = rank .. count
			end
		else 
			break
		end
	end
	local buffName, multi
	--Buff/debuff gained or count/rank changed
	for k,v in pairs(nPlayerAura) do
		if not oPlayerAura[k] or oPlayerAura[k] ~= v then
			multi = buffName
			buffName = k
		end
	end	
	--Buff/debuff lost
	for k,v in pairs(oPlayerAura) do
		if not nPlayerAura[k] then
			multi = buffName
			buffName = k
		end
		oPlayerAura[k] = nil
	end
	--Copy new table to old and clear
	for k,v in pairs(nPlayerAura) do
		oPlayerAura[k] = v
		nPlayerAura[k] = nil
	end
	if buffName then
		local updateSpell = not multi and PlayerAura[buffName].Spells

		if updatingSpell and updatingSpell == updateSpell then
			return
		end
		if updateSpell then
			updatingSpell = updateSpell
			self:ScheduleEvent("DrD_SpellUpdate", self.UpdateAB, 0.5, self, updateSpell)
		else
			self:CancelSpellUpdate()
			self:ScheduleEvent("DrD_FullUpdate", self.UpdateAB, 0.5, self)
		end
	end
end

function DrDamage:TargetAuraUpdate( changed )
	if self:IsEventScheduled("DrD_FullUpdate") and not changed then
		return
	end
	if self.Calculation["TargetAura"] then
		local update, spell = self.Calculation["TargetAura"]
		if update and not changed then
			return self:UpdateAB(spell)
		end
	end		
	if playerHealer then
		for i=1,40 do
			local name, rank, texture, count, _, _, _, unit = UnitBuff("target",i)
			if name then
				if TargetAura[name] then
					if unit and TargetAura[name].SelfCastBuff then
						if UnitIsUnit("player",unit) then
							nTargetAura[name] = rank .. count
						end
					else
						nTargetAura[name] = rank .. count
						--nTargetAura[name.."|"..texture] = rank .. count
					end
				end
			else break end
		end
	end
	for i=1,40 do
		local name, rank, texture, count, _, _, _, unit = UnitDebuff("target",i)
		if name then
			if TargetAura[name] then
				if unit and TargetAura[name].SelfCast then
					if UnitIsUnit("player",unit) then
						nTargetAura[name] = rank .. count
					end
				else
					nTargetAura[name] = rank .. count
					--nTargetAura[name.."|"..texture] = rank .. count
				end
			end
		else break end
	end
	local buffName, multi
	--Buff/debuff gained or count/rank changed
	for k,v in pairs(nTargetAura) do
		if not oTargetAura[k] or oTargetAura[k] ~= v then
			multi = buffName
			buffName = k
			--buffName = string_match(k,"[^|]+")
		end
	end	
	--Buff/debuff lost
	for k,v in pairs(oTargetAura) do
		if not nTargetAura[k] then
			multi = buffName
			buffName = k
			--buffName = string_match(k,"[^|]+")
		end
		oTargetAura[k] = nil
	end
	--Copy new table to old and clear
	for k,v in pairs(nTargetAura) do
		oTargetAura[k] = v
		nTargetAura[k] = nil
	end		
	if TargetAura[buffName] and not changed then
		local updateSpell = not multi and TargetAura[buffName].Spells

		if updatingSpell and updatingSpell == updateSpell then
			return
		end
		if updateSpell then
			updatingSpell = updateSpell
			self:ScheduleEvent("DrD_SpellUpdate", self.UpdateAB, 0.8, self, updateSpell )				
		else
			self:CancelSpellUpdate()
			self:ScheduleEvent("DrD_FullUpdate", self.UpdateAB, 1.0, self)
		end
	end
end

local lastState = GetTime()
function DrDamage:MODIFIER_STATE_CHANGED( state )
	if (state == "LALT" or state == "RALT") and settings.UpdateAlt or (state == "LCTRL" or state == "RCTRL") and settings.UpdateCtrl or (state == "LSHIFT" or state == "RSHIFT") and settings.UpdateShift then
		if GetTime() - lastState < 0.3 then
			if self:IsEventScheduled("DrD_FullUpdate_Modifier") then
				self:CancelScheduledEvent( "DrD_FullUpdate_Modifier" )
				self.visualChange = false
			end
		else
			if not self:IsEventScheduled("DrD_FullUpdate_Modifier") then
				self:ScheduleEvent("DrD_FullUpdate_Modifier", self.UpdateAB, 0.3, self)
			end
			self.visualChange = true
		end
		lastState = GetTime()
	end
end

local TalentIterate
function DrDamage:UpdateTalents(manual, all)
	if not manual then
		DrD_ClearTable( talents )
		for t = 1, GetNumTalentTabs() do	
			for i = 1, GetNumTalents(t) do
				local talentName, _, _, _, currRank, maxRank = GetTalentInfo(t, i)
				if (currRank ~= 0 or all) and talentInfo[talentName] then
					talents[talentName] = all and maxRank or currRank
				end	    	    
			end
		end
	end
	--Load talents into appropriate spell data
	for _, spell in pairs( spellInfo ) do
		TalentIterate( spell["Name"], spell[0], spell["Talents"] )
		if spell["Secondary"] then
			TalentIterate( spell["Secondary"]["Name"], spell["Secondary"][0], spell["Secondary"]["Talents"] )
		end
	end
	if not self:IsEventScheduled("DrD_FullUpdate") then
		self:UpdateAB()
	end
end

TalentIterate = function( name, baseSpell, endtable )
	if not baseSpell or type(baseSpell) == "function" then return end
	DrD_ClearTable( endtable )
	local melee = baseSpell.Melee
	local school = baseSpell.School
	local s1,s2,s3
	if type(school) == "table" then
		s1 = school[1]
		s2 = school[2]
		s3 = school[3]
	else
		s1 = school or "Physical"
	end
	for talentName, talentRank in pairs( talents ) do
		local talentTable = talentInfo[talentName]
		for i=1,#talentTable do
			local talent = talentTable[i]
			if (melee and talent.Melee or talent.Caster and not melee or (not talent.Melee and not talent.Caster)) and not (talent.Specials and (baseSpell.AutoAttack or baseSpell.AutoShot)) then
				local spells = talent.Spells
				if DrD_MatchData(spells,name,s3) or not baseSpell.NoSchoolTalents and DrD_MatchData(spells, "All", s1) or not baseSpell.NoTypeTalents and DrD_MatchData(spells, s2) then
					local number = #endtable + 1
					local modtype = talent.ModType
					local multiply = talent.Multiply
					endtable["Multiply"..number] = multiply
					endtable[number] = (type(talent.Effect) == "table") and talent.Effect[talentRank] or talent.Effect * talentRank
					if not modtype then
						endtable[("ModType"..number)] = multiply and "dmgM" or "dmgM_Add"
					elseif modtype == "SpellDamage" then
						endtable[("ModType"..number)] = multiply and "spellDmgM" or "spellDmgM_Add"
					else
						endtable[("ModType"..number)] = modtype
					end
					if talent.Mod then
						local mod = talent.Mod
						if DrD_MatchData(mod[1], name, "All", s1, s2, s3) then
							mod[6] = talentRank
							endtable["Mod"..number] = mod
						end
					end
				end
			end
		end
	end
end

local debug_flag

local SetItems = {}
function DrDamage:GetSetAmount(set)
	if debug_flag then
		return 8
	end
	if not updateSetItems and SetItems[set] then
		return SetItems[set]
	end
	
	if updateSetItems then
		DrD_ClearTable( SetItems )
		updateSetItems = false
	end
	
	local amount = 0
	local setData = self.SetBonuses[set]
	if setData then
		for _, itemID in ipairs(setData) do
			if IsEquippedItem(itemID) then
				amount = amount + 1
			end
		end
	end
	SetItems[set] = amount
	return amount
end

local Glyphs = {}
function DrDamage:HasGlyph(glyph)
	return debug_flag or Glyphs[glyph]
end

function DrDamage:UpdateGlyphs()
	DrD_ClearTable(Glyphs)
	for i=1,6 do
		local _, _, id = GetGlyphSocketInfo(i)
		if id then Glyphs[id] = true end
	end
end

function DrDamage:SetSpell( frame, slot )
	if settings.NeverTooltip or (settings.CombatTooltip and InCombatLockdown() or not settings.CombatTooltip) and (not settings.AlwaysTooltip and not (settings.AltTooltip and IsAltKeyDown()) and not (settings.CtrlTooltip and IsControlKeyDown()) and not (settings.ShiftTooltip and IsShiftKeyDown())) then
		return
	end
	
	local name, rank = GetSpellName(slot,BOOKTYPE_SPELL)
	
	if spellInfo[name] then
		local baseSpell = spellInfo[name][0]
		if type(baseSpell) == "function" then baseSpell = baseSpell() end

		if baseSpell and not baseSpell.NoTooltip then	
			if playerCaster or playerHybrid and not baseSpell.Melee then
				self:CasterTooltip( frame, name, rank )
			elseif playerMelee or playerHybrid and baseSpell.Melee then
				self:MeleeTooltip( frame, name, rank )
			end
		end
	end
end

function DrDamage:SetAction( frame, slot )
	self.hooks[frame].SetAction(frame, slot)
	
	if settings.NeverTooltip or (settings.CombatTooltip and InCombatLockdown() or not settings.CombatTooltip) and (not settings.AlwaysTooltip and not (settings.AltTooltip and IsAltKeyDown()) and not (settings.CtrlTooltip and IsControlKeyDown()) and not (settings.ShiftTooltip and IsShiftKeyDown())) then
		return
	end
	
	local gtype, pid = GetActionInfo(slot)
	local name, rank
		
	if (gtype == "spell" and pid ~= 0) then
		name, rank = GetSpellName(pid, gtype)
	elseif gtype == "macro" then
		name, rank = GetMacroSpell(pid)
	end

	if name and spellInfo[name] then
		local baseSpell = spellInfo[name][0]
		if type(baseSpell) == "function" then baseSpell = baseSpell() end

		if baseSpell and not baseSpell.NoTooltip then
			rank = rank or select(2,GetSpellInfo(name))
			if playerCaster or playerHybrid and not baseSpell.Melee then
				self:CasterTooltip( frame, name, rank )
			elseif playerMelee or playerHybrid and baseSpell.Melee then
				self:MeleeTooltip( frame, name, rank )
			end
		end
	end
end

local DrD_ProcessButton, DrD_CreateText, DrD_SpecialText
function DrDamage:UpdateAB(spell, uid, disable)
	--	Used for debugging updates.
	--[[
	if spell then
		self:Print( "Update AB: Spell" )
		
		if type( spell ) == "table" then
			for k in pairs( spell ) do
				self:Print( "Updating: " .. k )
			end
		else
			self:Print( "Updating: " .. spell )
		end
	elseif uid then
		self:Print( "Update AB: ID" )
		
	else
		self:Print( "Update AB: All" )
	end
	--]]
	
	self:TriggerEvent("DrDamage_Update", spell, uid)
	self:CancelSpellUpdate()
	
	if ABfunc then
		ABfunc()
	end
	if ABtable then
		for name, func in pairs(ABtable) do
			DrD_ProcessButton(_G[name], func, spell, uid)
		end	
	end
	if not ABdisable and settings.DefaultAB then
		for i, name in ipairs(ABobjects) do
			DrD_ProcessButton(name, ActionButton_GetPagedID, spell, uid, disable)
		end
	end				
	self.visualChange = nil
end

DrD_ProcessButton = function(button, func, spell, uid, disable)
	if not button then return end
	if not settings.ABText or disable then
		if button.drd then button.drd:Hide() end		
		return 
	end
	if button:IsVisible() then
		if not spell and not uid then
			local frame = button.drd
			if frame then frame:Hide() end
		end	
		local id, name, rank = func(button)
		if id and (not HasAction(id) or uid and uid ~= id) then return end
		DrDamage:CheckAction(button, spell, id, name, rank)
	end
end

function DrDamage:CheckAction(button, spell, id, name, rank)
	local gtype, pid
	if id then
		gtype, pid = GetActionInfo(id)

		if gtype == "spell" and (pid > 0) then
			name, rank = GetSpellName(pid, gtype)
		elseif gtype == "macro" then
			name, rank = GetMacroSpell(pid)
		end
	end
	if name then
		if spell and not DrD_MatchData(spell, name) then
			return
		end
		if self.ClassSpecials[name] then
			if rank and tonumber(rank) and GetSpellInfo(name) then
				rank = string_gsub(select(2,GetSpellInfo(name)),"%d+", rank)
			end		
			return DrD_SpecialText(self.ClassSpecials[name], rank, button)
		end
		if spellInfo[name] then
			local baseSpell = settings.SwapCalc and spellInfo[name]["Secondary"] and spellInfo[name]["Secondary"][0] or spellInfo[name][0]
			if type(baseSpell) == "function" then baseSpell = baseSpell() end

			if baseSpell then
				if gtype == "macro" and button then
					local macroText = _G[button:GetName().."Name"]
					if macroText then macroText:Hide() end
				end			

				local text, healingSpell

				if playerCaster or playerHybrid and not baseSpell.Melee then
					healingSpell = DrD_MatchData(baseSpell.School, "Healing")

					if (settings.CastsLeft and healingSpell or settings.CastsLeftDmg and not healingSpell) and not baseSpell.NoDPM and playerClass ~= "DEATHKNIGHT" then
						local _, _, _, manaCost = GetSpellInfo(name,rank)
						text = manaCost and (manaCost > 0) and math_floor(UnitPower("player",0) / manaCost) or "\226\136\158"
					else
						text = self:CasterCalc(name, rank)								
					end
				elseif playerMelee or playerHybrid and baseSpell.Melee then
					text = self:MeleeCalc(name, rank)
				end
				if type(text) == "number" then
					text = math_floor(text + 0.5)
				end
				if button then
					DrD_CreateText(text, button, healingSpell)
				elseif text then
					if healingSpell then
						return "|cff".. string_format("%02x%02x%02x", settings.FontColorHeal.r * 255, settings.FontColorHeal.g * 255, settings.FontColorHeal.b * 255).. text .. "|r"
					else
						return "|cff".. string_format("%02x%02x%02x", settings.FontColorDmg.r * 255, settings.FontColorDmg.g * 255, settings.FontColorDmg.b * 255).. text .. "|r"
					end
				end
			end
		end
	end
end

DrD_CreateText = function(text, button, healing, r, g, b )
	local drd = button.drd

	if not r and (DrDamage.visualChange or not drd) then
		local color = healing and settings.FontColorHeal or settings.FontColorDmg
		r,g,b = color.r, color.g, color.b
	end		
	
	if drd then
		if DrDamage.visualChange then
			drd:SetFont(DrD_Font, settings.FontSize, settings.FontEffect)
			drd:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", -10 + settings.FontXPosition, settings.FontYPosition + 5)
			drd:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 10 + settings.FontXPosition, settings.FontYPosition + 5)
			drd:SetTextColor(r,g,b)	
		end
		drd:SetText(text)
		drd:Show()
	else
		drd = button:CreateFontString("drd", "OVERLAY")
		button.drd = drd
		drd:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", -10 + settings.FontXPosition, settings.FontYPosition + 5)
		drd:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 10 + settings.FontXPosition, settings.FontYPosition + 5)
		drd:SetFont(DrD_Font, settings.FontSize, settings.FontEffect)
		drd:SetJustifyH("CENTER")
		drd:SetTextColor(r,g,b)
		drd:SetText(text)
		drd:Show()
	end
end

DrD_SpecialText = function( func, rank, button )
	local text, r, g, b = func(rank)
	if type(text) == "number" then text = math_floor(text + 0.5) end
	if text then
		if button then 
			DrD_CreateText(text, button, false, r, g, b)
		else
			if r then
				return "|cff".. string_format("%02x%02x%02x", r * 255, g * 255, b * 255) .. text .. "|r"
			else
				return "|cff".. string_format("%02x%02x%02x", settings.FontColorDmg.r * 255, settings.FontColorDmg.g * 255, settings.FontColorDmg.b * 255) .. text .. "|r"
			end
		end
	end
end

function DrDamage.BuffCalc( data, calculation, ActiveAuras, BuffTalentRanks, baseSpell, buffName, index, apps, texture, rank, target )
	local school = data.School
	if DrD_MatchData(data.Spells, calculation.name)
	or not calculation.healingSpell and (not school and not data.Spells or DrD_MatchData(school, calculation.school))
	or calculation.healingSpell and DrD_MatchData(school, "Healing")
	or (DrD_MatchData(school, "Spells") or DrD_MatchData(school, "Damage Spells") and not calculation.healingSpell) and (calculation.group ~= "Physical") and not calculation.physical
	or DrD_MatchData(school, calculation.group, calculation.subType) then
		if index then
			if data.SelfCast then
				local unit = select(8,UnitDebuff("target",index))
				if not unit or not UnitIsUnit("player",unit) then return end
			end
			if data.SelfCastBuff then
				local unit = select(8,UnitBuff((target or "target"),index))
				if not unit or not UnitIsUnit("player",unit) then return end
			end
		end	
		local modType = data.ModType
		if modType == "Special" and DrDamage.Calculation[buffName] then
			DrDamage.Calculation[buffName]( calculation, ActiveAuras, BuffTalentRanks, index, apps, texture, rank )
		else
			if data.ActiveAura then
				if data.Ranks then
					ActiveAuras[data.ActiveAura] = rank and tonumber(string_match(rank,"%d+")) or data.Ranks
				elseif data.Index then
					ActiveAuras[data.ActiveAura] = index or 0
				else
					if apps and apps > 0 then
						ActiveAuras[data.ActiveAura] = apps
					else
						ActiveAuras[data.ActiveAura] = ( ActiveAuras[data.ActiveAura] or 0 ) + 1
					end
				end
			end
			if data.Category then
				if ActiveAuras[data.Category] then return
				else ActiveAuras[data.Category] = true end
			end			
			--NOTE: For manually added buffs
			if not index and data.Mods then
				rank = rank and tonumber(string_match(rank,"%d+")) or data.Ranks
				if not UnitBuff("player", buffName) and (not data.Alt or data.Alt and not UnitBuff("player", data.Alt)) then
					for k, v in pairs(data.Mods) do
						if calculation[k] then
							if type(v) == "function" then
								calculation[k] = v(calculation[k],rank)
							else
								calculation[k] = calculation[k] + v
							end
						end
					end
				end				
			end
			if not data.Value then return end
			if data.Ranks then
				rank = rank and tonumber(string_match(rank,"%d+")) or data.Ranks
				local value = (type(data.Value) == "table") and data.Value[rank] or data.Value * rank
				if data.Apps then value = value * (apps or data.Apps) end
				if not modType then
					calculation.dmgM = calculation.dmgM * ( 1 + value )				
				elseif calculation[modType] then
					if data.Multiply then
						calculation[modType] = calculation[modType] * (1 + value)
					else
						calculation[modType] = calculation[modType] + value
					end
				end
			elseif data.Apps then
				apps = apps or data.Apps
				if not modType then
					calculation.dmgM = calculation.dmgM * ( 1 + apps * data.Value )	
				elseif calculation[modType] then
					if data.Multiply then
						calculation[modType] = calculation[modType] * (1 + apps * data.Value)
					else
						calculation[modType] = calculation[modType] + apps * data.Value
					end
				end
			elseif data.Texture and texture and string_find( texture, data.Texture ) then
				if not modType then
					calculation.dmgM = calculation.dmgM * ( 1 + data.Value )				
				elseif calculation[modType] then
					calculation[modType] = calculation[modType] + data.Value						
				end
			elseif not modType then
				calculation.dmgM = calculation.dmgM * (1 + data.Value)			
			elseif calculation[modType] then
				calculation[modType] = calculation[modType] + data.Value
			end			
		end
	end
end

--Credits to the author of RatingBuster (Whitetooth) for the formula!
local ratingTypes = { ["Hit"] = 8, ["Crit"] = 14, ["MeleeHit"] = 10, ["SpellHaste"] = 10 }
function DrDamage:GetRating( rType, convertR, full )
	local playerLevel = UnitLevel("player")
	local base = ratingTypes[rType]
	local rating, value

	if playerLevel > 70 then
		rating = base * (82/52) * ((131/63)^((playerLevel - 70)/10))
	elseif playerLevel > 60 then
		rating = base * 82 / (262 - 3 * playerLevel)
	elseif playerLevel > 10 then
		rating = base * ((playerLevel - 8) / 52)
	elseif 	playerLevel <= 10 then
		rating = base / 26
	end

	value = convertR and convertR/rating or rating
	value = full and value or DrD_Round(value,2)
	return value
end

function DrDamage:GetLevels()
	local targetLevel = UnitLevel("target")
	local playerLevel = UnitLevel("player")
	local boss
	
	if (UnitClassification("target") == "worldboss") then
		targetLevel = playerLevel + 3
		boss = true
	elseif targetLevel == -1 then
		targetLevel = playerLevel + 10
	end
	
	return math_min(10, targetLevel - playerLevel), playerLevel, targetLevel, boss
end

--CORE: Base spell hit chance
local hitDataMOB = { [-4] = 100, [-3] = 99, [-2] = 98, [-1] = 97, [0] = 96, 95, 94, 83, 72, 61, 50, 39, 28, 17, 6 }
local hitDataPlayer = { [-4] = 100, [-3] = 99, [-2] = 98, [-1] = 97, [0] = 96, 95, 94, 87, 80, 73, 66, 59, 52, 45, 38 }
local hitMod = (select(2, UnitRace("player")) == "Draenei") and 1 or 0
function DrDamage:GetSpellHit()
	local delta = math_max(-4,(settings.HitTarget or settings.HitTarget_M) and UnitExists("target") and (self:GetLevels()) or (settings.TargetLevel or settings.TargetLevel_M))	
	local npc = settings.HitTarget and not UnitIsPlayer("target") or not settings.HitTarget and not settings.TargetPlayer
	return (npc and hitDataMOB[delta] or hitDataPlayer[delta]) + hitMod + GetCombatRatingBonus(8)
end

local WeaponBuffScan = GetTime()
local WeaponBuff, WeaponBuffRank 
function DrDamage:GetWeaponBuff(off)
	local mh, _, _, oh = GetWeaponEnchantInfo()
	local name, rank, buff
	
	if not off and mh then
		if GetTime() > WeaponBuffScan then
			WeaponBuffScan = GetTime() + 2
			GT:SetInventoryItem("player", 16)
			_, _, buff = GT:Find("^([^%(]+) %(%d+ [^%)]+%)$", nil, nil, false, true)
			if buff then
				name, rank = string_match(buff,"^(.*) (%d+)$")
			end
			WeaponBuff, WeaponBuffRank = name or buff, rank
		end
		return WeaponBuff, WeaponBuffRank
	elseif off and oh then
		GT:SetInventoryItem("player", 17)
		_, _, name, rank = GT:Find("^([^%(]+) %(%d+ [^%)]+%)$", nil, nil, false, true)
		if buff then
			name, rank = string_match(buff,"^(.*) (%d+)$")
		end
	end
	return name or buff, rank
end

function DrDamage:Calc(name, rank, tooltip, modify, debug)
	if not spellInfo or not name then return end
	if not spellInfo[name] then return end
	local baseSpell = settings.SwapCalc and spellInfo[name]["Secondary"] and spellInfo[name]["Secondary"][0] or spellInfo[name][0]
	if type(baseSpell) == "function" then baseSpell = baseSpell() end
	if baseSpell then
		if playerCaster or playerHybrid and not baseSpell.Melee then
			return self:CasterCalc(name, rank, tooltip, modify, debug)								
		elseif playerMelee or playerHybrid and baseSpell.Melee then
			return self:MeleeCalc(name, rank, tooltip, modify, debug)
		end
	end
end

function DrDamage:TestAll()
	debug_flag = true
	local spells, ranks = 0, 0
	self:UpdateTalents(false,true)
	for k, v in pairs( spellInfo ) do
		local baseSpell = spellInfo[k][0]
		for i=1,#spellInfo[k] do
			if playerCaster or playerHybrid and not baseSpell.Melee then
				self:CasterCalc(k, i, true, nil, true)
				if not baseSpell.NoTooltip then self:CasterTooltip( GameTooltip, k, i ) end
			elseif playerMelee or playerHybrid and baseSpell.Melee then
				self:MeleeCalc(k, i, true, nil, true)
				if not baseSpell.NoTooltip then self:MeleeTooltip( GameTooltip, k, i ) end
			end
			ranks = ranks + 1
		end
		spells = spells + 1
	end
	debug_flag = false
	self:Print( "Tested: " .. ranks .. " ranks of " .. spells .. " spells.")
end

--[[
function DrDamage:GetModifier(school)
	local mod = 1
	for i=1,40 do
		local buffName, rank, _, apps = UnitBuff("player",i)
		if buffName then
			local buff = self.PlayerAura[buffName]
			if buff and DrD_MatchData(buff.School, school) and not buff.ModType and buff.Value then
				if buff.Ranks then
					rank = rank and tonumber(string_match(rank,"%d+")) or buff.Ranks
					local value = type(buff.Value) == "table" and buff.Value[rank] or buff.Value * rank
					mod = mod * ( 1 + value )					
				elseif buff.Apps then
					apps = apps or buff.Apps
					mod = mod * apps * buff.Value
				else
					mod = mod * (1 + buff.Value)
				end			
				
			end
		else break end
	end
	for i=1,40 do
		local buffName = UnitDebuff("target",i)
		if buffName then
			local buff = self.TargetAura[buffName]
			if buff and DrD_MatchData(buff.School, school) and not buff.ModType and buff.Value then
				if buff.Ranks then
					rank = rank and tonumber(string_match(rank,"%d+")) or buff.Ranks
					local value = type(buff.Value) == "table" and buff.Value[rank] or buff.Value * rank
					mod = mod * ( 1 + value )					
				elseif buff.Apps then
					apps = apps or buff.Apps
					mod = mod * apps * buff.Value
				else
					mod = mod * (1 + buff.Value)
				end
			end
		else break end
	end	
end
--]]

--[[ Used for harvesting base value increases:
function DrDamage:FindIDs(name)
	local stid = {}
	for i=1,100000 do
		local sname, srank = GetSpellInfo(i)
		srank = srank and string_match(srank,"%d+")
		if  name == sname and srank then
			stid[i] = true
		end
	end
	return stid
end

function DrDamage:HarvestValues(id)
	GT:SetHyperlink(GetSpellLink(id))
	local name, rank = GetSpellInfo(id)
	rank = rank and tonumber(string_match(rank,"%d+")) --or 1
	local min, max = string_match(GT:GetLine(GT:NumLines()),"(%d+) to (%d+)")
	if not min then 
		min = string_match(GT:GetLine(GT:NumLines()),"causing (%d+) ")
		min = min or string_match(GT:GetLine(GT:NumLines()),"Causes (%d+) ")
		min = min or string_match(GT:GetLine(GT:NumLines()),"doing (%d+) ")
		min = min or string_match(GT:GetLine(GT:NumLines()),"absorbing (%d+) ")
		min = min or string_match(GT:GetLine(GT:NumLines()),"Absorbs (%d+) ")
		min = min or string_match(GT:GetLine(GT:NumLines()),"deals (%d+) ")
		--min = min or string_match(GT:GetLine(GT:NumLines()),"for (%d+) ")
		--min = min or string_match(GT:GetLine(GT:NumLines()),"(%d+)%.")
		max = min
	end
	self:Print(name, rank, min, max)
	return name, rank, min, max
end

function DrDamage:Wowhead()
	--local stuff = ""
	--local table = {}
	--for k in string.gmatch(stuff,"spell=(%d+)") do
	--	table[k] = true
	--end
	--return table
	return { [47540] = true, [53005] = true, [53006] = true, [53007] = true }
end

local haxtable = {}
function DrDamage:Hax(value,force,remove,secondary)
	if value then
		local error
		if not remove then remove = 0 end
		DrD_ClearTable( haxtable )
		local entries = 0
		for k in pairs((type(value) == "table") and value or DrDamage:FindIDs(value)) do
			entries = entries + 1
			local name, rank, min, max = DrDamage:HarvestValues(k)
			local data = secondary and DrDamage.dump[name] and DrDamage.dump[name]["Secondary"] or DrDamage.dump[name]
			ChatFrame1:AddMessage(GetSpellLink(k))
			if name and rank and data and data[rank] and min and max then
				local oldmin, oldmax, spellLevel = data[rank][1], data[rank][2]
				if haxtable[rank] and ((haxtable[rank][1] ~= min) or (haxtable[rank][2] ~= max)) then
					self:Print("WARNING: Rank " .. rank .. " already exists in table with different values.")
					error = true
				else
					haxtable[rank] = { min, max, oldmin, oldmax, data[rank] }
				end
			end
		end
		self:Print("NOTE: Spell ID entries: " .. entries)
		if #haxtable > 0 then
			for i=1,#haxtable do
				local val = haxtable[i]
				if val then
					self:Print( "Rank " .. i .. ": " .. val[1] .. "-" .. val[2] .. " from " .. val[3] .. "-" .. val[4] )
				else
					self:Print( "Rank " .. i .. ": Error")
					error = true
				end
					
			end
			if not error or force then
				self:Print("Difference table:")
				for i=1,#haxtable do
					local val = haxtable[i]
					local rankstring = "[" .. i .. "]" .. " = { " .. val[3] .. ", " .. val[4] .. ", " .. (val[1] - val[3] - remove) .. ", " .. (val[2] - val[4] - remove)
					for k, v in pairs(val[5]) do
						if type(k) ~= "number" then
							rankstring = rankstring .. ", " .. k .. " = " .. v
						end
					end
					rankstring = rankstring .. ", },"
					ChatFrame1:AddMessage( rankstring ) 
				end
			end
		end
	end
end

DrDamage.dump = { }
--]]
